#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G24 -- S08 ------------------------------------------------------------##
__all__ = []


from .. import comparecards as cmp_cards
from .. import formlib as layout
from .. import helpstrings as hs
from .. import pilelib as stack
from .. import stackfill as fill
from ..forms import acesup as AcesUpLayout
from ..gamelib import game, state, info as gi
from ..hint import AbstractHint, CautiousDefaultHint
from ..packlib import info as cs
from ..piles import (yukon, spider)
from ..utillib.misc  import kwdefault
from . import canfield

GT, SL = gi.GT, gi.SL
Wrap = stack.StackWrapper


class TrustyTwelve_Hint(AbstractHint):

    def compute_hints(self):
        """
        compute_hints() -> None

        compute hints - main hint intelligence
        """
        rows = self._stacks['set']['rows']
        for from_stack in rows:
            for to_stack in (r for r in rows if from_stack is not r):
                card = to_stack.TOPCARD
                if to_stack.cardsBuild(from_stack, [card]):
                    score = 3000
                    if len(from_stack.cards) == 1:
                        score += 2000 + card['rank']
                        score += 1000 if len(to_stack.cards) > 1 else 0
                self.add_hint(score, 1, from_stack, to_stack)


class SS_RowStack(stack.FinishCloseMethod, stack.SS_RowStack):
    """
    Build down in rank by same suit. Shade full seq.
    """
    pass


class AC_RowStack(stack.FinishCloseMethod, stack.AC_RowStack):
    """
    Build down in rank by alt color suit. Shade full seq.
    """
    pass


class RK_RowStack(stack.FinishCloseMethod, stack.RK_RowStack):
    """
    Build down in rank alone. Shade full seq.
    """
    pass


class Yukon_SS_RowStack(stack.FinishCloseMethod, yukon.SS_RowStack):
    """
    Build down in rank by same suit. Play any. Shade full seq.
    """
    pass


class ShadeAC_RowStack(AC_RowStack):
    """
    Build down in rank by same suit. Shade full seq. _BOTTOM_IMAGE_ is shade.
    """
    _BOTTOM_IMAGE_ = 'shade'


class SRnSeqStackMethod(object):

    def _build_sequence(self, cards):
        """
        _build_sequence(cards:tup) -> bool

        :param tuple cards: cards to build from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        return (super(SRnSeqStackMethod, self)._build_sequence(cards) or
                cmp_cards.isSameRank(cards, rank=cards[0]['rank']))

    def _play_sequence(self, cards):
        """
        _build_sequence(cards:tup) -> bool

        :param tuple cards: cards to play from this pile
        :return: cards moveable?
        :rtype: boolean
        """
        return (super(SRnSeqStackMethod, self)._play_sequence(cards) or
                cmp_cards.isSameRank(cards, rank=cards[0]['rank']))


class SSSR_RowStack(SRnSeqStackMethod, SS_RowStack):

    @property
    def _helpString(self):
        """
        _helpString -> str

        :return: helpful string about pile for its canvas label.
        :rtype:  base_string
        """
        return hs.BUILD_SS_SR_DICT.get(cmp(self.cap['dir'], 0), '')


class Canfield_SSSR_RowStack(canfield.PlayMethod, SRnSeqStackMethod,
                             AC_RowStack):

    @property
    def _helpString(self):
        """
        _helpString -> str

        :return: helpful string about pile for its canvas label.
        :rtype:  base_string
        """
        return hs.BUILD_AC_SR_DICT.get(cmp(self.cap['dir'], 0), '')


class SSSR_RK_RowStack(SRnSeqStackMethod, spider.SS_RK_RowStack):

    @property
    def _helpString(self):
        """
        _helpString -> str

        :return: helpful string about pile for its canvas label.
        :rtype:  base_string
        """
        return '{0} {1}'.format(
                            hs.BUILD_RK_SR_DICT.get(cmp(self.cap['dir'], 0), ''),
                            hs.SPIDER_SS_SR_STR)


class Canfield_SS_RowStack(canfield.PlayMethod, SS_RowStack):

    def closeStack(self):
        """
        closeStack() -> None

        Perfom actions after an addCard() or insertCard(). Mainly foundation
        stacks subclass method to shade when pile has a completed sequence.
        """
        RANKS = len(self.game['ranks'])
        if len(self.cards) == RANKS * 2 and self._sequence(self.cards):
            self.is_filled = True
            self._shadeStack()


class Trillium(game.Game):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 13 horz N. Init deal 4 alt open card. Build down in rank by alt
      color. Play seq.

    _talon_
      Pile 1 SE. Deal 1 card on _row_.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _row_: AC_RowStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=342, name='Trillium', game_type=GT.SPIDER, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.CurdsWheyLayout
    _hint_ = spider.Hint
    _talon_ = stack.DealRowTalon
    _row_ = AC_RowStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=13, playcards=24) -> Layout
        """
        kwdefault(kw, rows=13, playcards=24)
        return super(Trillium, self)._create_game(**kw)

    def _start(self, flips=(False, True,) * 2, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: *flips* open.

        .. \_start([flips=(False, True,) * 2]) -> None
        """
        frames, rows = 0, len(flips)
        for flip in enumerate(flips):
            self.s['talon'].dealRow(frames=frames, flip=flip)
            if i  == rows - 1:
               self._play_sound('deal')
               frames = -1

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['rows'] if s.cards)


class Lily(Trillium):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 13 horz N. Init deal 4 alt open card. Build down in rank by alt
      color. Play seq. Base KING.

    _talon_
      Pile 1 SE. Deal 1 card on _row_.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _row_: AC_RowStack (base_rank=KING)
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=343, name='Lily', game_type=GT.SPIDER, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(AC_RowStack, base_rank=cs.KING)


class WakeRobin(Trillium):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 9 horz N. Init deal 4 alt open card. Build down in rank alone. Play
      seq.

    _talon_
      Pile 1 SE. Deal 1 card on _row_.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _row_: RK_RowStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=501, name='Wake-Robin', game_type=(GT.SPIDER |
                    GT.ORIGINAL), decks=2, redeals=0, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = RK_RowStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=9) -> Layout
        """
        kw.setdefault('rows', 9)
        return super(WakeRobin, self)._create_game(**kw)


class TripleWakeRobin(WakeRobin):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 13 horz N. Init deal 4 alt open card. Build down in rank alone.
      Play seq.

    _talon_
      Pile 1 SE. Deal 1 card on _row_.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _row_: RK_RowStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=502, name='Wake-Robin (3 Decks)', game_type=(GT.SPIDER
                    | GT.ORIGINAL), decks=3, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=13) -> Layout
        """
        kw.setdefault('rows', 13)
        return super(TripleWakeRobin, self)._create_game(**kw)


class Applegate(game.Game):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 7 horz N. Init deal 7 top [7, 5, 5, 5, 5, 5, 5] open cards. Build
      down in rank by same suit wrap. Play any. Base KING.

    _reserve_
      Pile 3 vert W. Init deal 1 card. Build none. Play top.

    _talon_
      Pile 1 NW. Deal 1 card on _row_.

    note:: ⟪highlight_play⟫ disabled
    ------
    :cvar Layout _layout_: layout.Trusty12Layout
    :cvar Hint _hint_: yukon.Hint
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _row_: Yukon_SS_RowStack (base_rank=KING, mod=13)
    :cvar Stack _reserve_: stack.OpenStack (max_accept=0)
    """
    __info__ = dict(id=382, name='Applegate', game_type=GT.SPIDER, decks=1,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.Trusty12Layout
    _talon_ = stack.InitialDealTalon
    _row_ = Wrap(Yukon_SS_RowStack, base_rank=cs.KING, mod=13)
    _reserve_ = Wrap(stack.OpenStack, max_accept=0)
    _hint_ = yukon.Hint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=7, reserves=3, playcards=16) -> Layout
        """
        kwdefault(kw, rows=7, reserves=3, playcards=16)
        return super(Applegate, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 7 ea top [7, 5, 5, 5, 5, 5, 5] open
          - s[reserves]: 1 ea open.

        .. \_start([]) -> None
        """
        for i in ((1,) * 2 + (7,) * 4):
            self.s['talon'].dealRow(rows=self.s['rows'][:i], flip=True,
                                    frames=0)
            self.s['talon'].dealRow(rows=self.s['rows'][i:], flip=False,
                                    frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['reserves'])

    @property
    def _highlight_piles(self):
        """
        Get stacks to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['rows'] if s.cards)


class Chelicera(Applegate):
    """
    Arrange seq on _row_ within a turn.

    _row_
      Pile 7 horz N. Init deal [4, 4, 4, 4, 7, 7, 7] open cards. Build down in
      rank by same suit. Play any. Gap deal 3 card from _talon_. Base KING.

    _talon_
      Pile 1 NW. Deal 1 card on _row_.

    note:: ⟪highlight_play⟫ disabled
    ------
    :cvar Layout _layout_: layout.Trusty12Layout
    :cvar Hint _hint_: yukon.Hint
    :cvar Stack _talon_: stack.Talon (num_cards=3)
    :cvar Stack _row_: Yukon_SS_RowStack (base_rank=KING)
    """
    __info__ = dict(id=344, name='Chelicera', game_type=GT.SPIDER, decks=1,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.Talon, num_deal=3)
    _row_ = Wrap(Yukon_SS_RowStack, base_rank=cs.KING)
    _hint_ = yukon.Hint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(reserves=0) -> Layout
        """
        kw.setdefault('reserves', 0)
        return super(Chelicera, self)._create_game(**kw)

    def _start(self, rows=7, **kw):
        """
        Deal initial cards for new game.
          - s[rows][:4]: *rows* - 3 ea open.
          - s[rows][4:]: *rows* ea open;.

        .. \_start([rows=7]) -> None
        """
        for i in range(rows - 3):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        for i in range(3):
            self.s['talon'].dealRow(rows=self.s['rows'][4:])

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_ with Talon.num_cards.

        *Parameters*:
            stack : Stack
              pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class WaveMotion(Trillium):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 8 horz. Build down in rank by same suit color. Play seq.

    _reserve_
      Pile 8 horz. Init deal [7, 7, 7, 7, 6, 6, 6, 6] open card. Build none.
      Play top.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _row_: SS_RowStack
    :cvar Stack _reserve_: stack.OpenStack
    """
    __info__ = dict(id=314, name='Wave Motion', game_type=(GT.SPIDER |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.80fc')
    __author__ = 'Skomoroh'
    __credits__ = ['David Bernazzani']
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _row_ = SS_RowStack
    _reserve_ = stack.OpenStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=8, reserves=8, playcards=12) -> Layout
        """
        kwdefault(kw, rows=8, reserves=8, playcards=12)
        return super(WaveMotion, self)._create_game(**kw)

    def _start(self, reserves=7, flip=True, **kw):
        """
        Deal initial cards for new game.
          - s[reserves]: ~ *rows* ea *flip* all open.

        .. \_start([flip=True, reserves=7]) -> None
        """
        for i in range(reserves - 2):
            self.s['talon'].dealRow(rows=self.s['reserves'], flip=flip,
                                    frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'][:4], flip=flip)
        self.s['talon'].dealRow(rows=self.s['reserves'])


class Flourish(WaveMotion):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 7 horz. Build down in rank by alt color. Play seq.

    _reserve_
      Pile 8 horz. Init deal [7, 7, 7, 7, 6, 6, 6, 6] open card. Build none.
      Play top.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _row_: AC_RowStack
    :cvar Stack _reserve_: stack.OpenStack
    """
    __info__ = dict(id=753, name='Flourish', game_type=(GT.SPIDER | GT.OPEN |
                    GT.ORIGINAL), decks=1, redeals=0, version='1.10fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = AC_RowStack


class FourStacks(game.Game):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 10. Init deal [13, 13, 13, 13, 0, 0, 0, 0, 0, 0] cards. Build down
      in rank by alt color. Play seq.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.InitialDealTalon
    :cvar Stack _row_: ShadeAC_RowStack
    """
    __info__ = dict(id=335, name='Four Stacks', game_type=(GT.SPIDER |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.CurdsWheyLayout
    _talon_ = stack.InitialDealTalon
    _row_ = ShadeAC_RowStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=10, texts=False, playcards=16) -> Layout
        """
        kwdefault(rows=10, texts=False, playcards=16)
        return super(FourStacks, self)._create_game(**kw)

    def _start(self, rows=13, **kw):
        """
        Deal initial cards for new game.
          - s[rows][:4]: *rows* ea open.

        .. \_start([rows=13]) -> None
        """
        for i in range(rows - 3):
            self.s['talon'].dealRow(rows=self.s['rows'][:4], frames=0)
        self._play_sound('deal')
        for i in range(3):
            self.s['talon'].dealRow(rows=self.s['rows'][:4])

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['rows'] if s.cards)


class CurdsAndWhey(game.Game):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 13 horz. Init deal 4 open card. Build down in rank by same rank or
      same suit. Play seq. Base KING.

    seealso::
      http://en.wikipedia.org/wiki/Curds_and_Whey
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.InitialDealTalon
    :cvar Stack _row_: SSSR_RowStack (base_rank=KING)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=294, name='Curds and Whey', game_type=(GT.SPIDER |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.80fc')
    __author__ = 'Skomoroh'
    __credits__ = ['David Parlett']
    __version__ = 7.70

    _layout_ = layout.CurdsWheyLayout
    _talon_ = stack.InitialDealTalon
    _row_ = Wrap(SSSR_RowStack, base_rank=cs.KING)
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=13, texts=False, playcards=16) -> Layout
        """
        kwdefault(rows=13, texts=False, playcards=16)
        return super(CurdsAndWhey, self)._create_game(**kw)

    def _start(self, rows=4, flip=True, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: *rows* ea *flip* all open.

        .. \_start([flip=True, rows=4]) -> None
        """
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['rows'] if s.cards)


class MissMuffet(CurdsAndWhey):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 10 horz. Init deal [6, 5, 5, 5, 5, 5, 5, 5, 5, 6] open card. Build
      down in rank by same rank or same suit. Play seq. Base KING.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.InitialDealTalon
    :cvar Stack _row_: SSSR_RowStack (base_rank=KING)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=349, name='Miss Muffet', game_type=(GT.SPIDER |
                    GT.OPEN), decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Bill Taylor']
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=10) -> Layout
        """
        kw.setdefault('rows', 10)
        return super(MissMuffet, self)._create_game(**kw)

    def _start(self, rows=6, **kw):
        """
        Deal initial cards for new game.
          - s[rows][0::9]: *rows* ea *flip* all open.
          - s[rows][1:9]: *rows* - 1 ea *flip* all  open.

        .. \_start([flip=True, rows=6]) -> None
        """
        kw.setdefault('rows', rows - 1)
        super(MissMuffet, self)._start(**kw)
        self.s['talon'].dealRow(rows=self.s['rows'][0::9])


class Nordic(MissMuffet):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 10 horz. Init deal [6, 5, 5, 5 ,5, 5, 5, 5 ,5, 6] open card. Build
      down in rank by same rank or same suit. Play seq.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.InitialDealTalon
    :cvar Stack _row_: SSSR_RowStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=352, name='Nordic', game_type=(GT.SPIDER | GT.OPEN |
                    GT.ORIGINAL), decks=1, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = SSSR_RowStack


class Dumfries(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz S _foundation. Init deal 1 card. Build down in rank by same
      rank or alt color. Play top or all.

    _talon_
      Pile 1 NW. Build None. Play top.

    _foundation_
      Pile 4 horz N. Build up in rank by same suit. Play top.
    ------
    :cvar Layout _layout_: layout.KlondikeLayout
    :cvar Stack _talon_:  stack.OpenAutoTalon
    :cvar Stack _foundation_:  stack.SS_Foundation
    :cvar Stack _row_: Canfield_SSSR_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=311, name='Dumfries', game_type=GT.ONE_DECK, decks=1,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.KlondikeLayout
    _talon_ = stack.OpenAutoTalon
    _foundation_ = stack.SS_Foundation
    _row_ = Canfield_SSSR_RowStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.
        Add talon to dropstacks.

        .. \_create_game(rows=8, texts=True, playcards=20) -> Layout
        """
        kwdefault(kw, rows=8, texts=True, playcards=20)
        l, s = super(Dumfries, self)._create_game(**kw), self.s
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 1 ea open.
          - s[talon]: top open.

        .. \_start([]) -> None
        """
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].fill_stack()


class Galloway(Dumfries):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 7 horz S _foundation. Init deal 1 card. Build down in rank by same
      rank or alt color. Play top or all.

    _talon_
      Pile 1 NW. Build None. Play top.

    _foundation_
      Pile 4 horz N. Build up in rank by same suit. Play top.
    ------
    :cvar Layout _layout_: layout.KlondikeLayout
    :cvar Stack _talon_:  stack.OpenAutoTalon
    :cvar Stack _foundation_:  stack.SS_Foundation
    :cvar Stack _row_: Canfield_SSSR_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=312, name='Galloway', game_type=GT.ONE_DECK,
                    decks=1, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.80fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=7) -> Layout
        """
        kw.setdefault('rows', 7)
        return super(Galloway, self)._create_game(**kw)


class Robin(Dumfries):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 12 horz S _foundation. Init deal 1 card. Build down in rank by same
      rank or alt color. Play top or all.

    _talon_
      Pile 1 NW. Build None. Play top.

    _foundation_
      Pile 8 horz N. Build up in rank by same suit. Play top.
    ------
    :cvar Layout _layout_: layout.KlondikeLayout
    :cvar Stack _talon_:  stack.OpenAutoTalon
    :cvar Stack _foundation_:  stack.SS_Foundation
    :cvar Stack _row_: Canfield_SSSR_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=313, name='Robin', game_type=(GT.TWO_DECK |
                    GT.ORIGINAL), decks=2, redeals=0, version='0.80fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=12) -> Layout
        """
        kw.setdefault('rows', 12)
        return super(Robin, self)._create_game(**kw)


class Arachnida(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz S _foundation. Init deal [6, 6, 6, 6, 5, 5, 5, 5, 5, 5]
      card. Build down in rank by same rank or alone. Play seq by same rank or
      same suit. Seq by same suit autodrop on _foundation_.

    _talon_
      Pile 1 NW. Build None. Deal 1 card on _row_ while no gap.

    _row_
      Pile 1 horz NE. Build none. Play none.
    ------
    :cvar Layout _layout_: AcesUpLayout.AcesUpLayout
    :cvar Stack _talon_:  stack.SpiderTalon
    :cvar Stack _foundation_:  stack.SingleFoundation
    :cvar Stack _row_: SSSR_RK_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=348, name='Arachnida', game_type=GT.SPIDER, decks=2,
                    redeals=0, skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = AcesUpLayout.AcesUpLayout
    _talon_ = stack.SpiderTalon
    _foundation_ = stack.SingleFoundation
    _row_ = SSSR_RK_RowStack

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=10, playcards=16) -> Layout
        """
        kwdefault(kw, rows=10, playcards=16)
        return super(Arachnida, self)._create_game(**kw)

    def _start(self, rows=6, flip=False, **kw):
        """
        Deal initial cards for new game.
          - s[rows][:4]: *rows* ea *flip* all open.
          - s[rows][4:]: *rows* - 1 ea *flip* all open.

        .. \_start([flip=False, rows=6]) -> None
        """
        for i in range(rows - 2):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['rows'][:4], flip=flip)
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        for row in (r for r in self.s['rows'] if len(r.cards) >= 13):
            if row._sequence(r.cards[-13:]):
                self.move(13, r, self.s['foundations'][0])


class Harvestman(Arachnida):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz S _foundation. Init deal [6, 6, 6, 6, 5, 5, 5, 5, 5, 5]
      card. Build down in rank by same rank or same suit. Play seq. Seq by
      same suit autodrop on _foundation_.

    _talon_
      Pile 1 NW. Build None. Deal 1 card on _row_ while no gap.

    _row_
      Pile 1 horz NE. Build none. Play none.
    ------
    :cvar Layout _layout_: layout.KlondikeLayout
    :cvar Stack _talon_:  stack.SpiderTalon
    :cvar Stack _foundation_:  stack.SingleFoundation
    :cvar Stack _row_: SSSR_RK_RowStack
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=534, name='Harvestman', game_type=(GT.SPIDER |
                    GT.ORIGINAL), decks=2, redeals=0,
                    skill_level=SL.MOSTLY_SKILL, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = SSSR_RowStack


class GermanPatience(game.Game):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 8 horz. Init deal 1 card. Build up in rank alone wrap. Play top.
      Max 13 cards.

    _talon_
      Pile 1 SW. Build None. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build None. Play top.

    seealso::
        http://en.wikipedia.org/wiki/German_Patience
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.WasteTalon
    :cvar Stack _waste_:  stack.WasteStack
    :cvar Stack _row_: RK_RowStack (max_cards=13, mod=13,
      dir=1, max_move=1)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=414, name='German Patience', game_type=GT.SPIDER,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.CurdsWheyLayout
    _talon_ = stack.WasteTalon
    _waste_ = stack.WasteStack
    _row_ = Wrap(RK_RowStack, max_cards=13, mod=13,
                         dir=1, max_move=1)
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=8, playcards=14, align_talon='sw') -> Layout
        """
        kwdefault(kw, rows=8, playcards=14, align_talon='sw')
        return super(GermanPatience, self)._create_game(**kw)

    def _move_start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 1 ea open.

        .. \_start([]) -> None
        """
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @property
    def is_complete(self):
        return all(s.is_filled for s in self.s['rows'] if s.cards)


class BavarianPatience(GermanPatience):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 10 horz. Init deal 1 card. Build up in rank alone wrap. Play top.
      Max 13 cards.

    _talon_
      Pile 1 SW. Build None. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build None. Play top.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.WasteTalon
    :cvar Stack _waste_:  stack.WasteStack
    :cvar Stack _row_: RK_RowStack (max_cards=13, mod=13,
      dir=1, max_move=1)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=415, name='Bavarian Patience', game_type=GT.SPIDER,
                    decks=2, redeals=0, skill_level=SL.MOSTLY_SKILL,
                    version='0.90fc')
    __author__ = 'Skomoroh'
    __credits__ = ['Thomas Warfield']
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=10) -> Layout
        """
        kw.setdefault('rows', 10)
        return super(BavarianPatience, self)._create_game(**kw)


class EightPacks(BavarianPatience):
    """
    Arrange cards on _row_ within 3 turn.

    _row_
      Pile 10. Init deal 1 card. Build up in rank by same suit. Play seq.

    _talon_
      Pile 1 SW. Build None. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build None. Play top.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_:  stack.WasteStack
    :cvar Stack _row_: RK_RowStack (dir=1)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=724, name='Eight Packs', game_type=(GT.SPIDER |
                    GT.ORIGINAL), decks=2, redeals=2, version='1.10fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=3)
    _row_ = Wrap(SS_RowStack, dir=1)


class FourPacks(EightPacks):
    """
    Arrange cards on _row_ within 2 turn.

    _row_
      Pile 10 horz. Init deal 1 card. Build down in rank by same suit. Play
      top or all.

    _talon_
      Pile 1 SW. Build None. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build None. Play top.
    ------
    :cvar Layout _layout_: layout.CurdsWheyLayout
    :cvar Stack _talon_:  stack.WasteTalon (max_rounds=2)
    :cvar Stack _waste_:  stack.WasteStack
    :cvar Stack _row_: Canfield_SS_RowStack (mod=13)
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=762, name='Four Packs', game_type=GT.SPIDER, decks=2,
                    redeals=1, skill_level=SL.MOSTLY_SKILL, version='2.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=2)
    _row_ = Wrap(Canfield_SS_RowStack, mod=13)

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(playcards=18) -> Layout
        """
        kw.setdefault('playcards', 18)
        return super(FourPacks, self)._create_game(**kw)


class TrustyTwelve(game.Game):
    """
    Play all cards on _row_ within a turn.

    _row_
      Pile 12 horz. Init deal 1 card. Build down in rank alone. Play top. Gap
      deal 1 card from _talon_.

    _talon_
      Pile 1 SW. Build None. Deal none.
    ------
    :cvar Layout _layout_: layout.Trusty12Layout
    :cvar Stack _talon_:  stack.Talon
    :cvar Stack _row_: stack.RK_RowStack (max_move=1)
    :cvar Hint _hint_: TrustyTwelve_Hint
    """
    __info__ = dict(id=480, name='Trusty Twelve', game_type=GT.ONE_DECK,
                    decks=1, redeals=0, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = layout.Trusty12Layout
    _talon_ = stack.Talon
    _row_ = Wrap(stack.RK_RowStack, max_move=1)
    _hint_ = TrustyTwelve_Hint

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=12, playcards=12) -> Layout
        """
        kwdefault(kw, rows=12, playcards=12)
        return super(TrustyTwelve, self)._create_game(**kw)

    def _start(self, **kw):
        """
        Deal initial cards for new game.
          - s[rows]: 1 ea open.

        .. \_start([]) -> None
        """
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])

    @property
    def is_complete(self):
        return not self.s['talon'].cards


class KnottyNines(TrustyTwelve):
    """
    Play all cards on _row_ within a turn.

    _row_
      Pile 9. Init deal 1 card. Build down in rank alone. Play top. Gap deal
      1 card from _talon_.

    _talon_
      Pile 1 SW. Build None. Deal none.
    ------
    :cvar Layout _layout_: layout.Trusty12Layout
    :cvar Stack _talon_:  stack.Talon
    :cvar Stack _row_: stack.RK_RowStack (max_move=1)
    :cvar Hint _hint_: TrustyTwelve_Hint
    """
    __info__ = dict(id=481, name='Knotty Nines', game_type=GT.ONE_DECK,
                    decks=1, redeals=0, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=9) -> Layout
        """
        kw.setdefault('rows', 9)
        return super(KnottyNines, self)._create_game(**kw)


class SweetSixteen(TrustyTwelve):
    """
    Arrange cards on _row_ within a turn.

    _row_
      Pile 16 over 2 row. Init deal 1 card. Build down in rank by alt color.
      Play top. Gap deal 1 card from _talon_.

    _talon_
      Pile 1 SW. Build None. Deal none.
    ------
    :cvar Layout _layout_: layout.Trusty12Layout
    :cvar Stack _talon_:  stack.Talon
    :cvar Stack _row_: stack.AC_RowStack (max_move=1)
    :cvar Hint _hint_: TrustyTwelve_Hint
    """
    __info__ = dict(id=482, name='Sweet Sixteen', game_type=GT.ONE_DECK,
                    decks=1, redeals=0, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = Wrap(stack.AC_RowStack, max_move=1)

    def _create_game(self, **kw):
        """
        Create game format.

        Augment pile placements based on current form and group attributes.

        .. \_create_game(rows=16, ncols=8, playcards=10) -> Layout
        """
        kwdefault(kw, rows=16, ncols=8, playcards=10)
        return super(SweetSixteen, self)._create_game(**kw)


PROVIDES = [WaveMotion, Flourish, Applegate, Trillium, Lily, Chelicera,
           WakeRobin, TripleWakeRobin, FourStacks, CurdsAndWhey, Dumfries,
           Galloway, Robin, Arachnida, MissMuffet, Nordic, GermanPatience,
           BavarianPatience, TrustyTwelve, KnottyNines, SweetSixteen,
           Harvestman, EightPacks, FourPacks]